package com.quick.mq.config;
import com.alibaba.fastjson2.JSON;
import com.quick.common.Component.ExchangeType;
import com.quick.common.Exception.exception.BaseException;
import com.quick.common.bean.Car;
import com.quick.common.bean.Driver;
import com.quick.common.bean.Order;
import com.quick.common.bean.User;
import com.quick.common.dto.MessageDTO;
import com.quick.common.dto.WaitOrderDTO;
import com.quick.common.service.*;
import com.quick.common.util.EmailUtil;
import com.rabbitmq.client.Channel;
import io.lettuce.core.RedisConnectionException;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
import java.util.List;

@Component
public class ListenerConfig {
    @DubboReference
    private IOrderService orderService;
    @DubboReference
    private IMQService mqService;
    @DubboReference
    private IUserService userService;
    @DubboReference
    private ICarService carService;
    @DubboReference
    private IDriverService driverService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 1. 创建订单队列
    @RabbitListener(queues = "call_car_queue")
    public void callCarQueue(String message, Channel channel, Message msg) throws IOException {
        // 获取消息的唯一标识 deliveryTag
        long deliveryTag = msg.getMessageProperties().getDeliveryTag();
        // 1. 获取当前重试次数（首次重试为0）
        Integer retryCount = (Integer) msg.getMessageProperties().getHeaders().get("retry-count");
        if (retryCount == null) {
            retryCount = 0;
        }
        // 2. 重试次数阈值（建议3次，可配置在配置文件中）
        int maxRetryCount = 3;
        try {
            //反序列化获取order对象
            Order order = JSON.parseObject(message, Order.class);
            if (order == null) {
                throw new BaseException(400,"接收的Order为null");
            }
            // 生成订单
            int orderId = orderService.addOrder(order);
            order.setId(orderId);


            //订单id存入redis
            Integer id=order.getId();
            stringRedisTemplate.opsForValue().set("user:"+order.getUser_id() +"order:" ,id.toString());


            //序列化
            String order1=JSON.toJSONString(order);
            //发送待派单队列
            mqService.sendMessage("wait_order.driver", order1);

            // 发送广播通知+
            MessageDTO messageDTO = MessageDTO.builder()
                    .userNotify("订单创建完成，等待派单")
                    .userEmail(userService.getUserById(order.getUser_id()).getEmail())
                    .build();

            String s = JSON.toJSONString(messageDTO);
            mqService.sendMessage(ExchangeType.fanoutExchange, "", s);
            //处理成功：手动 ACK，告知 RabbitMQ 删除消息
            channel.basicAck(deliveryTag, false);
            System.out.println("消息处理成功，已手动ACK");
        } catch (Exception e) {
            if (isRetryableException(e)&&retryCount < maxRetryCount){
                // 3. 重试次数未超阈值：更新重试次数，放回队列
                msg.getMessageProperties().getHeaders().put("retry-count", retryCount + 1);
                // 可重试异常（如网络波动）：拒绝消息并放回队列（限制重试次数）
                channel.basicNack(deliveryTag, false, true);
                System.err.println("消息处理失败，已放回队列重试");
            }else {
                // 不可重试异常（如数据库异常）：拒绝消息,不放回队列（放入死信队列）
                channel.basicReject(deliveryTag, false);
                System.err.println("消息处理失败，已拒绝并进入死信队列");
            }
        }
        System.out.println("订单创建成功");
    }
    // 叫车请求队列
    @RabbitListener(queues = "wait_order_queue")
    public void waitOrderQueue(String message,Channel  channel, Message msg) throws IOException {
        long deliveryTag = msg.getMessageProperties().getDeliveryTag();
        Integer retryCount = (Integer) msg.getMessageProperties().getHeaders().get("retry-count");
        if (retryCount == null) {
            retryCount = 0;
        }
        int maxRetryCount = 3;
        try {
            Order order = JSON.parseObject(message, Order.class);
            // 校验经纬度非空
            if (order.getStart_longitude() == null || order.getStart_latitude() == null) {
                throw new BaseException(400,"订单经纬度为空");
            }

            System.out.println("订单经纬度：" + order.getStart_longitude() + "," + order.getStart_latitude());
            // 查询附近5公里司机列表
            List<Driver> drivers = driverService.nearByFreeDrivers(
                    order.getStart_longitude(),
                    order.getStart_latitude()
            );

            if (drivers.isEmpty()) {
                // 无司机：发送通知
                MessageDTO messageDTO = MessageDTO.builder()
                        .userNotify("附近无空闲司机")
                        .userEmail(userService.getUserById(order.getUser_id()).getEmail())
                        .build();
                String s = JSON.toJSONString(messageDTO);
                mqService.sendMessage(ExchangeType.fanoutExchange, "",s);
            } else {
                // 有司机：发送派单队列
                WaitOrderDTO waitOrderDTO = new WaitOrderDTO();
                waitOrderDTO.setOrder(order);
                waitOrderDTO.setDriverList(drivers);
                String s1 = JSON.toJSONString(waitOrderDTO);
                mqService.sendMessage("dispatch_order.driver", s1);

                // 发送通知
                MessageDTO messageDTO = MessageDTO.builder()
                        .userNotify("正在等待司机接单")
                        .userEmail(userService.getUserById(order.getUser_id()).getEmail())
                        .build();
                        String s2 = JSON.toJSONString(messageDTO);
                mqService.sendMessage(ExchangeType.fanoutExchange, "", s2);
            }
            channel.basicAck(deliveryTag, false);
            System.out.println("消息处理成功，已手动ACK");
        } catch (Exception e) {
            if (isRetryableException(e)&&retryCount < maxRetryCount){
                // 3. 重试次数未超阈值：更新重试次数，放回队列
                msg.getMessageProperties().getHeaders().put("retry-count", retryCount + 1);
                // 可重试异常（如网络波动）：拒绝消息并放回队列（限制重试次数）
                channel.basicNack(deliveryTag, false, true);
                System.err.println("消息处理失败，已放回队列重试");
            }else {
                // 不可重试异常（如数据库异常）：拒绝消息,不放回队列（放入死信队列）
                channel.basicReject(deliveryTag, false);
                System.err.println("消息处理失败，已拒绝并进入死信队列");
            }
        }
        System.out.println("请您耐心等待接单处理");
    }

    // 派单执行队列
    @RabbitListener(queues = "dispatch_order_queue")
    public void dispatchOrderQueue(String message,Channel  channel, Message msg) throws IOException {
        long deliveryTag = msg.getMessageProperties().getDeliveryTag();
        // 1. 获取当前重试次数（首次重试为0）
        Integer retryCount = (Integer) msg.getMessageProperties().getHeaders().get("retry-count");
        if (retryCount == null) {
            retryCount = 0;
        }
        // 2. 重试次数阈值（建议3次，可配置在配置文件中）
        int maxRetryCount = 3;
        try {
            WaitOrderDTO waitOrderDTO = JSON.parseObject(message, WaitOrderDTO.class);
            if (waitOrderDTO == null || waitOrderDTO.getDriverList().isEmpty() || waitOrderDTO.getOrder() == null) {
                throw new BaseException(400,"WaitOrderDTO参数不完整，跳过处理");
            }
            // 筛选第一个司机派单，默认最近的司机接单
            Driver driver = waitOrderDTO.getDriverList().get(0);
            if (driver == null){
                throw new BaseException(400,"司机不存在");
            }
            //todo 应该向司机发送邀请，如果司机拒绝，在这里写拒绝后的逻辑，将司机从列表移除，直到有司机接单
            Order order = waitOrderDTO.getOrder();
            // 修改司机状态和订单状态
            driverService.editStatus(driver.getId(),  (byte)1);

            //设置接单司机的id
            orderService.setDriver(order.getId(), driver.getId());
            //修改订单状态
            orderService.editStatus(order.getId(), 1);
            // 查询用户信息
            Integer userId = order.getUser_id();
            User user = userService.getUserById(userId);
            if (user == null) {
                throw new BaseException(400,"用户不存在，user_id=" + userId);
            }
            //查询司机车辆信息
            Car car = carService.getCar(driver.getCar_id());

            // 发送通知
            MessageDTO messageDTO = MessageDTO.builder()
                    .userNotify("已匹配司机:" + driver.getName()+",手机号:"+driver.getPhone()+",车辆型号:"+
                            car.getColor() +car.getBrand()+car.getModel()+",车牌号:"+car.getPlate())
                    .userEmail(user.getEmail())
                    .driverNotify("您已接单用户" + user.getName() + "，手机号:" + user.getPhone())
                    .driverEmail(driver.getEmail())
                    .build();
            String s = JSON.toJSONString(messageDTO);
            mqService.sendMessage(ExchangeType.fanoutExchange, "", s);
            channel.basicAck(deliveryTag, false);
            System.out.println("消息处理成功，已手动ACK");
        } catch (Exception e) {
            if (isRetryableException(e)&&retryCount < maxRetryCount){
                // 3. 重试次数未超阈值：更新重试次数，放回队列
                msg.getMessageProperties().getHeaders().put("retry-count", retryCount + 1);
                // 可重试异常（如网络波动）：拒绝消息并放回队列（限制重试次数）
                channel.basicNack(deliveryTag, false, true);
                System.err.println("消息处理失败，已放回队列重试");
            }else {
                // 不可重试异常（如数据库异常）：拒绝消息,不放回队列（放入死信队列）
                channel.basicReject(deliveryTag, false);
                System.err.println("消息处理失败，已拒绝并进入死信队列");
            }
        }
        System.out.println("已为您派单最近的司机");
    }


    @RabbitListener(queues = "user_notify_queue")
    public void userNotifyQueue(String message, Channel channel, Message msg) throws IOException {
        long deliveryTag = msg.getMessageProperties().getDeliveryTag(); // 新增：获取deliveryTag
        try {
            MessageDTO messageDTO = JSON.parseObject(message, MessageDTO.class);
            System.out.println("用户通知：" + messageDTO.getUserNotify());
            if (messageDTO.getUserEmail() != null){
                EmailUtil.sendEmail(messageDTO.getUserEmail(),messageDTO.getUserNotify());
            }
        }finally {
                channel.basicAck(deliveryTag, false);
        }
    }

    @RabbitListener(queues = "driver_notify_queue")
    public void driverNotifyQueue(String message, Channel channel, Message msg) throws IOException {
        long deliveryTag = msg.getMessageProperties().getDeliveryTag();
        try {
            MessageDTO messageDTO = JSON.parseObject(message, MessageDTO.class);
            System.out.println("司机通知：" + messageDTO.getDriverNotify());
            System.out.println("司机邮箱：" + messageDTO.getDriverEmail());
            if (messageDTO.getDriverEmail() != null){
                EmailUtil.sendEmail(messageDTO.getDriverEmail(),messageDTO.getDriverNotify());
            }
        }finally {
            channel.basicAck(deliveryTag, false);
        }
    }

    // 死信队列监听器
    @RabbitListener(queues = "dead_letter_queue")
    public void deadLetterQueueListener(String message, Channel channel, Message msg) throws IOException {
        try {
            // 打印死信消息详情（便于排查问题）
            System.err.println("=== 收到死信消息 ===");
            System.err.println("死信内容：" + message);
            System.err.println("来源业务队列：" + msg.getMessageProperties().getReceivedExchange());
            System.err.println("重试次数：" + msg.getMessageProperties().getHeaders().get("retry-count"));
            System.err.println("==================");
        } finally {
            // 死信处理完成后，手动 ACK（避免死信队列堆积）
            channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);
        }
    }
    // 判断是否为可重试异常（自定义逻辑）
    private boolean isRetryableException(Exception e) {
        //Dubbo 超时、数据库连接异常等可重试
        return e instanceof TimeoutException
                || e instanceof SQLException
                || e instanceof RpcException // Dubbo 远程调用异常
                || e instanceof RedisConnectionException
                || e.getCause() instanceof TimeoutException
                || e.getCause() instanceof SQLException
                || e.getCause() instanceof RpcException
                || e.getCause() instanceof RedisConnectionException
                ;
    }
}